home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / Information / Mac Programming Secrets 1.0.1 / Chapter 06 / Neat Stuff.c < prev    next >
C/C++ Source or Header  |  1992-05-19  |  21KB  |  684 lines

  1. #include "Standard Stuff.h"
  2. #include "Neat Stuff.h"
  3. #include "MouseTracker.h"
  4.  
  5. const RGBColor    kRGBBlack = {0, 0, 0};
  6. const RGBColor    kRGBWhite = {0xFFFF, 0xFFFF, 0xFFFF};
  7.  
  8. Pattern            gAntsPattern = {0x1F, 0x3E, 0x7C, 0xF8, 0xF1, 0xE3, 0xC7, 0x8F};
  9. long            gLastAntMarch;
  10. const short        kMarchTime = 1;
  11.  
  12.  
  13. /*******************************************************************************
  14.  
  15.     DoMarchingAnts
  16.  
  17.     This routine is used to do the actual marching of the ants (animate  the
  18.     selection rectangle). It should be called periodically, like in the main
  19.     event loop of the application.
  20.  
  21.     The routine first makes a few checks. It makes sure that we have the
  22.     animation turned on, that we have a front window, that an adequate time
  23.     has passed since the last time we animated the selection, and that there
  24.     is indeed a selection rectangle for the window. If all those condition
  25.     hold, we animate the selection rectangle. This is done by shifting all of
  26.     the horizontal lines of the pattern downwards by a single line. The line
  27.     at the bottom is moved to the top of the pattern. This gives the following
  28.     transformation:
  29.  
  30.                     Start                Result
  31.                     ========            ========
  32.                     11111000 ---\        01111100 <--+
  33.                     11110001     \---->    11111000    |
  34.                     11100011 ---\        11110001    |
  35.                     11000111     \---->    11100011    |
  36.                     10001111 ---\        11000111    |
  37.                     00011111     \---->    10001111    |
  38.                     00111110 ---\        00011111    |
  39.                     01111100 -\     \---->    00111110    |
  40.                                \                    |
  41.                                 \-------------------+
  42.  
  43.     Once we have the new pattern, we redraw the selection rectangle with it.
  44.  
  45. *******************************************************************************/
  46. void    DoMarchingAnts(void)
  47. {
  48.     MyWindowPtr    theWindow;
  49.     Rect        antsRect;
  50.     short        i;
  51.     unsigned char    lastPart;
  52.  
  53.     theWindow = (MyWindowPtr) FrontWindow();
  54.     if (theWindow && theWindow->antsOnTheMarch && (TickCount() > gLastAntMarch + kMarchTime)) {
  55.         antsRect = theWindow->selectionRect;
  56.         if (!EmptyRect(&antsRect)) {
  57.             lastPart = gAntsPattern[7];
  58.             for (i = 7; i > 0; --i)
  59.                 gAntsPattern[i] = gAntsPattern[i-1];
  60.             gAntsPattern[0] = lastPart;
  61.             PenNormal();
  62.             PenPat(gAntsPattern);
  63.             SetPort((GrafPtr) theWindow);
  64.             FrameRect(&antsRect);
  65.             gLastAntMarch = TickCount();
  66.         }
  67.     }
  68. }
  69.  
  70.  
  71. /*******************************************************************************
  72.  
  73.     KillAnts
  74.  
  75.     Turn the selection rectangle animation off. We would want to do this, for
  76.     example, if the application is put into the background.
  77.  
  78. *******************************************************************************/
  79. void    KillAnts(MyWindowPtr whichWindow)
  80. {
  81.     Rect            theRect;
  82.  
  83.     whichWindow->antsOnTheMarch = FALSE;
  84.     theRect = whichWindow->selectionRect;
  85.     RefreshWindow(whichWindow, &theRect);
  86. }
  87.  
  88.  
  89. /*******************************************************************************
  90.  
  91.     StartAnts
  92.  
  93.     Turn the ants on. We would want to call this routine whenever our
  94.     application moves from the background into the foreground.
  95.  
  96. *******************************************************************************/
  97. void    StartAnts(MyWindowPtr whichWindow)
  98. {
  99.     whichWindow->antsOnTheMarch = TRUE;
  100. }
  101.  
  102.  
  103. /*******************************************************************************
  104.  
  105.     DoOpenWindow
  106.  
  107.     Open a MacPaint file and show it in a window. Call Standard File to let
  108.     the user select a MacPaint file (such files have a file type of ‘PNTG’).
  109.     If the user doesn’t select Cancel, we create a window for the picture,
  110.     read the file into an offscreen grafPort, attach the offscreen buffer to
  111.     the window, and finally show the window.
  112.  
  113. *******************************************************************************/
  114. void    DoOpenWindow(void)
  115. {
  116.     MyWindowPtr            newWindow;
  117.     SFTypeList            types = {'PNTG'};
  118.     StandardFileReply    reply;
  119.  
  120.     StandardGetFile((FileFilterProcPtr) NIL, 1, types, &reply);
  121.     if (reply.sfGood) {
  122.         newWindow = CreateBufferedWindow(72*8, 720, 1);
  123.         if (newWindow != NIL) {
  124.             ReadMacPaint(&reply.sfFile, newWindow->offWorld);
  125.             ShowWindow((WindowPtr) newWindow);
  126.         }
  127.     }
  128. }
  129.  
  130.  
  131. /*******************************************************************************
  132.  
  133.     DoCloseWindow
  134.  
  135.     Called when the user selects the “Close” menu item or clicks on the GoAway
  136.     box. Disposes of the offscreen grafPort used to buffer the picture, and
  137.     then closes the window and disposes of its data structures.
  138.  
  139. *******************************************************************************/
  140. void    DoCloseWindow(WindowPtr theWindow)
  141. {
  142.     DisposeGWorld(((MyWindowPtr) theWindow)->offWorld);
  143.     DisposeWindow(theWindow);
  144. }
  145.  
  146.  
  147. /*******************************************************************************
  148.  
  149.     DoDrawGrowIcon
  150.  
  151.     Draw the grow icon. We do this in such a way that the scrollbar lines are
  152.     not drawn. Normally, when the Toolbox routine DrawGrowIcon is called,
  153.     vertical and horizontal lines are also drawn indicating where the
  154.     scrollbars will be drawn. We don’t have scrollbars, so we don’t want those
  155.     lines drawn. We avoid them by setting the clipping region of the window to
  156.     include the grow box only. We then call DrawGrowIcon, and restore the old
  157.     clipping region before returning.
  158.  
  159. *******************************************************************************/
  160. void    DoDrawGrowIcon(WindowPtr theWindow)
  161. {
  162.     Rect        iconRect;
  163.     RgnHandle    oldClip;
  164.  
  165.     SetPort(theWindow);
  166.     oldClip = NewRgn();
  167.     GetClip(oldClip);
  168.  
  169.     iconRect = theWindow->portRect;
  170.     iconRect.top = iconRect.bottom - 15;
  171.     iconRect.left = iconRect.right - 15;
  172.     ClipRect(&iconRect);
  173.  
  174.     PenNormal();
  175.     DrawGrowIcon(theWindow);
  176.  
  177.     SetClip(oldClip);
  178.     DisposeRgn(oldClip);
  179. }
  180.  
  181. /*******************************************************************************
  182.  
  183.     DoActivateWindow
  184.  
  185.     Take care of a window that needs to be activated or deactivated. Normally,
  186.     this means removing the highlighting used for the current selection. Since
  187.     we _do_ hilite the current selection with Marching Ants, we need to turn
  188.     them on or off.
  189.  
  190. *******************************************************************************/
  191. void    DoActivateWindow(WindowPtr theWindow, Boolean activating)
  192. {
  193.     if (activating)
  194.         StartAnts((MyWindowPtr) theWindow);
  195.     else
  196.         KillAnts((MyWindowPtr) theWindow);
  197. }
  198.  
  199.  
  200. /*******************************************************************************
  201.  
  202.     DoUpdateWindow
  203.  
  204.     Called to update any areas of the window that may have been clobbered by
  205.     other windows. We get the bounding rectangle of the update region (which
  206.     has been incorporated into the visRgn of the window by a previous call to
  207.     BeginUpdate), and pass that into a RefreshWindow routine that does the
  208.     actual drawing.
  209.  
  210. *******************************************************************************/
  211. void    DoUpdateWindow(EventRecord *theEvent)
  212. {
  213.     MyWindowPtr        theWindow;
  214.     Rect            rectToCopy;
  215.  
  216.     theWindow = (MyWindowPtr) theEvent->message;
  217.     rectToCopy = (**(theWindow->windowRecord.port.visRgn)).rgnBBox;
  218.  
  219.     RefreshWindow(theWindow, &rectToCopy);
  220. }
  221.  
  222.  
  223. /*******************************************************************************
  224.  
  225.     DoContentClick
  226.  
  227.     Called when the user clicks in the content area of our window. Clicks on
  228.     the grow box are detected and handled elsewhere, so we don’t have to worry
  229.     about them.
  230.  
  231.     See if the user clicked within the current selection rectangle. If so,
  232.     drag the contents of that rectangle around the window. If not, get rid of
  233.     the old selection and drag out a new one.
  234.  
  235. *******************************************************************************/
  236. Rect    DoContentClick(EventRecord *theEvent, WindowPtr whichWindow)
  237. {
  238.     Point        location;
  239.     Rect        newSelection;
  240.  
  241.     location = theEvent->where;
  242.     GlobalToLocal(&location);
  243.     if (PointInSelection(whichWindow, location)) {
  244.         DragSelection(whichWindow, location);
  245.     } else {
  246.         KillAnts((MyWindowPtr) whichWindow);
  247.         newSelection = SketchNewRect(FALSE);
  248.         ((MyWindowPtr) whichWindow)->selectionRect = newSelection;
  249.         StartAnts((MyWindowPtr) whichWindow);
  250.     }
  251. }
  252.  
  253.  
  254. /*******************************************************************************
  255.  
  256.     PointInSelection
  257.  
  258.     Utility used to determine if a given point (in the window’s local
  259.     coordinates) is within the window’s selection rectangle. This routine is
  260.     called from DoContentClick.
  261.  
  262. *******************************************************************************/
  263. Boolean    PointInSelection(WindowPtr whichWindow, Point location)
  264. {
  265.     Rect    selectionRect;
  266.  
  267.     selectionRect = ((MyWindowPtr) whichWindow)->selectionRect;
  268.     return PtInRect(location, &selectionRect);
  269. }
  270.  
  271.  
  272. /*******************************************************************************
  273.  
  274.     RefreshWindow
  275.  
  276.     Workhorse that copies a section of the offscreen buffer to the window.
  277.     This routine is called from a couple of places, including the update
  278.     routines, and the Marching Ant routines.
  279.  
  280.     The pointer to our offscreen buffer is stored after the normal window
  281.     record. We retrieve it, get a reference to its PixMap, and lock the bits
  282.     so that they don’t move during the call to CopyBits. We then make sure
  283.     that the foreground color is set to black, and that the background color
  284.     is set to white. If they are set to any other values, then “colorization”
  285.     occurs according to Inside Mac V page 71 and Technote #163.
  286.  
  287. *******************************************************************************/
  288. void    RefreshWindow(MyWindowPtr theWindow, Rect *theRect)
  289. {
  290.     GrafPtr            oldPort;
  291.     Rect            rectToCopy;
  292.     GWorldPtr        offWorld;
  293.     PixMapHandle    offPixMap;
  294.  
  295.     GetPort(&oldPort);
  296.     SetPort((GrafPtr) theWindow);
  297.  
  298.     offWorld = theWindow->offWorld;
  299.     offPixMap = GetGWorldPixMap(offWorld);
  300.     SectRect(theRect, &(**offPixMap).bounds, &rectToCopy);
  301.  
  302.     (void) LockPixels(offPixMap);
  303.     RGBForeColor(&kRGBBlack);
  304.     RGBBackColor(&kRGBWhite);
  305.  
  306.     CopyBits(&(( (GrafPtr) offWorld)->portBits),
  307.              &theWindow->windowRecord.port.portBits,
  308.              &rectToCopy, &rectToCopy, srcCopy, NIL);
  309.  
  310.     UnlockPixels(offPixMap);
  311.     SetPort(oldPort);
  312. }
  313.  
  314.  
  315. /*******************************************************************************
  316.  
  317.     CreateBufferedWindow
  318.  
  319.     Creates a window that’s backed up by an offscreen buffer. We do this by
  320.     first defining an extended window record. In the same way that a window
  321.     record contains an embedded GrafPort, we define a record with and embedded
  322.     WindowRecord. This record includes a pointer to an offscreen buffer and a
  323.     rectangle for keeping track of the current selection.
  324.  
  325.     We start by creating an offscreen buffer using NewGWorld (available with
  326.     32-bit Color QuickDraw under 6.0.x, and built into 7.0). If that succeeds,
  327.     we clear out the buffer with a call to EraseRect, and then start to create
  328.     the window. We dynamically allocate the extended window record with NewPtr
  329.     and pass the result to GetNewWindow. If we successfully create the window,
  330.     we copy in the pointer to the  offscreen buffer and initialize the
  331.     selection rectangle.
  332.  
  333. *******************************************************************************/
  334. MyWindowPtr    CreateBufferedWindow(short width, short height, short depth)
  335. {
  336.     Rect        boundsRect;
  337.     CTabHandle    aColorTable = NIL;
  338.     GDHandle    aGDevice = NIL;
  339.     GWorldFlags    flags = 0;
  340.  
  341.     MyWindowPtr    newWindow;
  342.     GWorldPtr    offscreenWorld;
  343.     CGrafPtr    oldPort;
  344.     GDHandle    oldDevice;
  345.  
  346.     SetRect(&boundsRect, 0, 0, width, height);
  347.  
  348.     if (NewGWorld(&offscreenWorld, depth, &boundsRect, aColorTable,
  349.              aGDevice, flags) != noErr)
  350.         return NIL;
  351.  
  352.     GetGWorld(&oldPort, &oldDevice);
  353.     SetGWorld(offscreenWorld, NIL);
  354.     EraseRect(&qd.thePort->portRect);
  355.     SetGWorld(oldPort, oldDevice);
  356.  
  357.     newWindow = (MyWindowPtr) NewPtr(sizeof(MyWindowRecord));
  358.     if (newWindow != NIL)
  359.         newWindow = (MyWindowPtr) GetNewWindow(kNewWindowID,
  360.                                             (Ptr) newWindow,
  361.                                             (WindowPtr) -1);
  362.  
  363.     if (newWindow == NIL) {
  364.         DisposeGWorld(offscreenWorld);
  365.     } else {
  366.         newWindow->offWorld = offscreenWorld;
  367.         newWindow->selectionRect.top = 10;
  368.         newWindow->selectionRect.left = 10;
  369.         newWindow->selectionRect.bottom = 90;
  370.         newWindow->selectionRect.right = 90;
  371.         newWindow->antsOnTheMarch = FALSE;
  372.     }
  373.  
  374.     return newWindow;
  375. }
  376.  
  377.  
  378. /*******************************************************************************
  379.  
  380.     ReadMacPaint
  381.  
  382.     Reads in a MacPaint file according to Technote #86. We open the file, skip
  383.     over the first 512 bytes, and read the rest of the file into a buffer.
  384.     This buffer now contains the packed MacPaint image. By making successive
  385.     calls to  UnpackBits, we unpack the image into our offscreen buffer.
  386.  
  387. *******************************************************************************/
  388. void        ReadMacPaint(FSSpec* theFile, GWorldPtr buffer)
  389. {
  390.     short    refNum;
  391.     long    fileSize;
  392.     short    scanline;
  393.     Ptr        sourceBuffer;
  394.     Ptr        sourcePtr;
  395.     Ptr        destPtr;
  396.     short    rowBytes;
  397.     char    mmuMode;
  398.  
  399.     if (FSpOpenDF(theFile, fsRdPerm, &refNum) != noErr)
  400.         return;
  401.  
  402.     if (GetEOF(refNum, &fileSize) != noErr) {
  403.         FSClose(refNum);
  404.         return;
  405.     }
  406.  
  407.     if (SetFPos(refNum, fsFromStart, 512) != noErr) {
  408.         FSClose(refNum);
  409.         return;
  410.     }
  411.  
  412.     fileSize -= 512;
  413.     sourceBuffer = NewPtr(fileSize);
  414.     if (sourceBuffer == NIL) {
  415.         FSClose(refNum);
  416.         return;
  417.     }
  418.  
  419.     if (FSRead(refNum, &fileSize, sourceBuffer) != noErr) {
  420.         DisposePtr(sourceBuffer);
  421.         FSClose(refNum);
  422.         return;
  423.     }
  424.     FSClose(refNum);
  425.  
  426.     sourcePtr = sourceBuffer;
  427.     destPtr = GetPixBaseAddr(GetGWorldPixMap(buffer));
  428.     rowBytes = (**(GetGWorldPixMap(buffer))).rowBytes & 0x7FFF;
  429.  
  430.     mmuMode = true32b;
  431.     SwapMMUMode(&mmuMode);
  432.     for (scanline = 0 ; scanline < 720 ; ++scanline) {
  433.         UnpackBits(&sourcePtr, &destPtr, 72);
  434.         destPtr += (rowBytes - 72);
  435.     }
  436.     SwapMMUMode(&mmuMode);
  437.  
  438.     DisposePtr(sourceBuffer);
  439. }
  440.  
  441.  
  442. /*******************************************************************************
  443.  
  444.     Drag variables - these are used in the routine that drags around the
  445.     currently selected part of the picture.
  446.  
  447. *******************************************************************************/
  448.  
  449. Point        startPoint;        /* The point where the user mouse-downed.        */
  450. Rect        startRect;        /* The initial selection rectangle.                */
  451. Rect        previousRect;    /* The last location of the dragged selection.    */
  452. Rect        currentRect;    /* The current location of same.                */
  453. GWorldPtr    sourceBuffer;    /* Buffer that holds the original image.        */
  454. GWorldPtr    destBuffer;        /* Buffer holding modified image.                */
  455. WindowPtr    gTheWindow;        /* Reference to the window we are munging.        */
  456.  
  457.  
  458. /*******************************************************************************
  459.  
  460.     DragSelection
  461.  
  462.     Called to drag around the current selection. This function is really just
  463.     a front end to the real workhorse, TrackMouse(). It sets up some variables
  464.     that we’ll need later, and then calls TrackMouse(). TrackMouse() tracks
  465.     the mouse, and calls a callback routine we passed to it when needed. This
  466.     callback merely has to take care of drawing the selected image in the
  467.     right place. When TrackMouse() returns, we make note of the final location
  468.     of the dragged image, dispose of our temporary buffer, and return.
  469.  
  470. *******************************************************************************/
  471. void    DragSelection(WindowPtr whichWindow, Point mouse)
  472. {
  473.     Point    dummyStart;
  474.     Point    dummyEnd;
  475.  
  476.     gTheWindow = whichWindow;
  477.     startPoint = mouse;
  478.     startRect = ((MyWindowPtr) whichWindow)->selectionRect;
  479.     previousRect = startRect;
  480.     destBuffer = ((MyWindowPtr) whichWindow)->offWorld;
  481.     sourceBuffer = CloneGWorld(destBuffer);
  482.  
  483.     if (sourceBuffer != NIL) {
  484.         TrackMouse(nil, DragSelectionFeedback, nil, &dummyStart, &dummyEnd);
  485.         ((MyWindowPtr) whichWindow)->selectionRect = currentRect;
  486.         DisposeGWorld(sourceBuffer);
  487.     }
  488. }
  489.  
  490.  
  491. /*******************************************************************************
  492.  
  493.     DragSelectionFeedback
  494.  
  495.     This routine is called by TrackMouse() to provide dragging feedback. Here,
  496.     we check to see if the mouse moved. If so, we calculate where the
  497.     selection rectangle should be given the current location of the mouse. If
  498.     the new selection rectangle is different than the previous rectangle, we
  499.     update our working buffer from information stored in our original,
  500.     pristine (“I’m not _that_ pristine!”  -- Molly Ringwald) buffer.
  501.  
  502. *******************************************************************************/
  503. void    DragSelectionFeedback(Point anchorPoint,
  504.                  Point nextPoint,
  505.                  Boolean turnItOn,
  506.                  Boolean mouseDidMove)
  507. {
  508. #pragma unused (turnItOn)
  509.  
  510.     Rect    tempRect;
  511.  
  512.     if (mouseDidMove) {
  513.         PointToCurrentRect(nextPoint);
  514.         if (!EqualRect(¤tRect, &previousRect)) {
  515.             UpdateBuffers();
  516.             UnionRect(&previousRect, ¤tRect, &tempRect);
  517.             RefreshWindow((MyWindowPtr)gTheWindow, &tempRect);
  518.             previousRect = currentRect;
  519.         }
  520.     }
  521. }
  522.  
  523.  
  524. /*******************************************************************************
  525.  
  526.     PointToCurrentRect
  527.  
  528.     This function is used to figure out the where the selected image should be
  529.     positioned. When the mousedown occurred, we noted the location of the
  530.     mouse click and the original location of the selection rectangle. Using
  531.     the current location of the mouse, we figure out the new location of the
  532.     selection rectangle. This is done by first figuring the offset of the
  533.     current mouse position to the starting position. We then offset the
  534.     selection rectangle by the same amount. Once that is done, we make sure
  535.     that it’s pinned to the window’s bounds.
  536.  
  537. *******************************************************************************/
  538. void    PointToCurrentRect(Point currentPoint)
  539. {
  540.     Point    delta;
  541.     Rect    portRect;
  542.  
  543.     delta = currentPoint;
  544.     SubPt(startPoint, &delta);
  545.     currentRect = startRect;
  546.     portRect = gTheWindow->portRect;
  547.     OffsetRect(¤tRect, delta.h, delta.v);
  548.  
  549.     SetPt(&delta, 0, 0);
  550.     if (currentRect.left < portRect.left)
  551.         delta.h = portRect.left - currentRect.left;
  552.     if (currentRect.right > portRect.right)
  553.         delta.h = portRect.right - currentRect.right;
  554.     if (currentRect.top < portRect.top)
  555.         delta.v = portRect.top - currentRect.top;
  556.     if (currentRect.bottom > portRect.bottom)
  557.         delta.v = portRect.bottom - currentRect.bottom;
  558.  
  559.     OffsetRect(¤tRect, delta.h, delta.v);
  560. }
  561.  
  562.  
  563. /*******************************************************************************
  564.  
  565.     UpdateBuffers
  566.  
  567.     During our dragging, we maintain two offscreen buffers. The first holds
  568.     the picture as it was before we starting dragging. The second holds the
  569.     image as we would like it to appear in the window. This second buffer is
  570.     created from the first one. When we first start dragging, the second
  571.     buffer is identical to the first. When we need to show the image being
  572.     dragged around, we perform the following 3 steps:
  573.  
  574.         1. Using the first buffer, restore the background in the second
  575.            buffer where the dragged image used to be. This location is
  576.            stored in “previousRect.”
  577.         2. Erase the rectangle that shows where the image started from.
  578.            This location is stored in “startRect.”
  579.         3. Using the first buffer, draw the dragged image where it should
  580.            currently be shown. This location is stored in “currentRect.”
  581.  
  582. *******************************************************************************/
  583. void    UpdateBuffers(void)
  584. {
  585.     PixMapHandle    sourceMap;
  586.     PixMapHandle    destMap;
  587.  
  588.     CGrafPtr        oldPort;
  589.     GDHandle        oldDevice;
  590.  
  591.     sourceMap = GetGWorldPixMap(sourceBuffer);
  592.     destMap = GetGWorldPixMap(destBuffer);
  593.  
  594.     (void) LockPixels(sourceMap);
  595.     (void) LockPixels(destMap);
  596.  
  597.     GetGWorld(&oldPort, &oldDevice);
  598.     SetGWorld(destBuffer, NIL);
  599.  
  600.     RGBForeColor(&kRGBBlack);
  601.     RGBBackColor(&kRGBWhite);
  602.  
  603.     CopyBits(&(((GrafPtr) sourceBuffer)->portBits),
  604.          &(((GrafPtr) destBuffer)->portBits),
  605.          &previousRect, &previousRect, srcCopy, NIL);
  606.  
  607.     EraseRect(&startRect);
  608.  
  609.     CopyBits(&(((GrafPtr) sourceBuffer)->portBits),
  610.          &(((GrafPtr) destBuffer)->portBits),
  611.          &startRect, ¤tRect, srcCopy, NIL);
  612.  
  613.     SetGWorld(oldPort, oldDevice);
  614.  
  615.     UnlockPixels(sourceMap);
  616.     UnlockPixels(destMap);
  617. }
  618.  
  619.  
  620. /*******************************************************************************
  621.  
  622.     CloneGWorld
  623.  
  624.     Given a GWorldPtr, clone it entirely and return the pointer to the new
  625.     GWorld. This routine is called when we start dragging and we need to
  626.     manipulate two buffers that are initially identical.
  627.  
  628.     The cloning is performed by first getting some of the characteristics of
  629.     the GWorld we are cloning. We get the source’s GDeviceHandle, its bit
  630.     depth, its color table, its set of gdFlags, and its bounding rectangle.
  631.     Using this information, we make a call to NewGWorld to create another
  632.     GWorld with the same characteristics. If the call to NewGWorld succeeds,
  633.     we copy the source’s image into the clone with a call to CopyBits.
  634.  
  635. *******************************************************************************/
  636. GWorldPtr    CloneGWorld(GWorldPtr sourceGWorld)
  637. {
  638.     GDHandle        aGDevice;
  639.     short            depth;
  640.     CTabHandle        aColorTable;
  641.     GWorldFlags        flags;
  642.     Rect            bounds;
  643.     GWorldPtr        newGWorld;
  644.  
  645.     PixMapHandle    sourceMap;
  646.     PixMapHandle    destMap;
  647.  
  648.     CGrafPtr        oldPort;
  649.     GDHandle        oldDevice;
  650.  
  651.     aGDevice = GetGWorldDevice(sourceGWorld);
  652.     depth = (**(**aGDevice).gdPMap).pixelSize;
  653.     aColorTable = (**(**aGDevice).gdPMap).pmTable;
  654.     HandToHand((Handle*) &aColorTable);
  655.     flags = (**aGDevice).gdFlags;
  656.     bounds = (**aGDevice).gdRect;
  657.  
  658.     if (NewGWorld(&newGWorld, depth, &bounds, aColorTable, aGDevice, flags) != noErr)
  659.         return NIL;
  660.  
  661.     sourceMap = GetGWorldPixMap(sourceGWorld);
  662.     destMap = GetGWorldPixMap(newGWorld);
  663.  
  664.     (void) LockPixels(sourceMap);
  665.     (void) LockPixels(destMap);
  666.  
  667.     GetGWorld(&oldPort, &oldDevice);
  668.     SetGWorld(newGWorld, NIL);
  669.  
  670.     RGBForeColor(&kRGBBlack);
  671.     RGBBackColor(&kRGBWhite);
  672.  
  673.     CopyBits(&(((GrafPtr) sourceGWorld)->portBits),
  674.              &(((GrafPtr) newGWorld)->portBits),
  675.              &bounds, &bounds, srcCopy, NIL);
  676.  
  677.     SetGWorld(oldPort, oldDevice);
  678.  
  679.     UnlockPixels(sourceMap);
  680.     UnlockPixels(destMap);
  681.  
  682.     return newGWorld;
  683. }
  684.